package com.example.dayu521.sg_oss.service.impl;

import com.example.dayu521.sg_oss.conf.prop.MyMinioProperties;
import com.example.dayu521.sg_oss.service.BaseServiceProvider;
import com.example.dayu521.sg_oss.service.Oss;
import io.minio.*;
import io.minio.errors.*;
import io.minio.http.Method;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.example.dayu521.exception.BadObjSize;
import org.example.dayu521.exception.NoSuchOss;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

@Service
@ConditionalOnProperty(prefix = "sg.oss", name ="backend" ,havingValue="minio" ,matchIfMissing=false)
@EnableConfigurationProperties({MyMinioProperties.class})
@Slf4j
public class MinioOss implements Oss {

    @Autowired
    public void setBUCKET(MyMinioProperties myMinioProperties) {
        this.myMinioProperties = myMinioProperties;
    }

    private MyMinioProperties myMinioProperties;

    @Resource
    private MinioClient minioClient;

    @Override
    public void put_obj(String obj_name, InputStream obj_src, long obj_size){
        try {
            var res = minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(myMinioProperties.getBucket())
                            .object(obj_name)
                            .stream(obj_src, obj_size, -1)
                            .build());
        } catch (ErrorResponseException e) {
            throw new RuntimeException(e);
        } catch (InsufficientDataException e) {
            throw new RuntimeException(e);
        } catch (InternalException e) {
            throw new RuntimeException(e);
        } catch (InvalidKeyException e) {
            throw new RuntimeException(e);
        } catch (InvalidResponseException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        } catch (ServerException e) {
            throw new RuntimeException(e);
        } catch (XmlParserException e) {
            throw new RuntimeException(e);
        }

    }

    @Override
    public InputStream get_obj(String obj_name) throws NoSuchOss {

        try {
            return minioClient.getObject(GetObjectArgs.builder()
                    .bucket(myMinioProperties.getBucket())
                    .object(obj_name)
                    .build());
        } catch (ErrorResponseException e) {
            throw new RuntimeException(e);
        } catch (InsufficientDataException e) {
            throw new RuntimeException(e);
        } catch (InternalException e) {
            throw new RuntimeException(e);
        } catch (InvalidKeyException e) {
            throw new NoSuchOss(e);
        } catch (InvalidResponseException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        } catch (ServerException e) {
            throw new RuntimeException(e);
        } catch (XmlParserException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public boolean rm_obj(String obj_name) {
        try {
            minioClient.removeObject(RemoveObjectArgs.builder()
                    .bucket(myMinioProperties.getBucket())
                    .object(obj_name).build());
        } catch (ErrorResponseException e) {
            throw new RuntimeException(e);
        } catch (InsufficientDataException e) {
            throw new RuntimeException(e);
        } catch (InternalException e) {
            throw new RuntimeException(e);
        } catch (InvalidKeyException e) {
            throw new RuntimeException(e);
        } catch (InvalidResponseException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        } catch (ServerException e) {
            throw new RuntimeException(e);
        } catch (XmlParserException e) {
            throw new RuntimeException(e);
        }
        return true;
    }

    @Override
    public String oss_identify() {
        return "minio";
    }

    @Override
    public void ask_for_help(BaseServiceProvider baseServiceProvider) {
        baseServiceProvider.minio(this);
    }

    record ObjectUrlArgsRecord(Method method, Map<String, String> headers) {
    }

    record AskParam(
            String obj_name,
            long obj_size
    ){}
    record AskResult(
            String method,
            String url
    ){}
    private static final Map<String, ObjectUrlArgsRecord> HttpMethod = Map.of("get", new ObjectUrlArgsRecord(Method.GET, Map.of()),
            "post", new ObjectUrlArgsRecord(Method.POST, Map.of())
            , "put", new ObjectUrlArgsRecord(Method.PUT, Map.of()));
    public AskResult ask_obj_handle(AskParam askParam) throws BadObjSize {
        var file_size= Optional.of(askParam.obj_size()).orElse(0l);
        if(file_size> myMinioProperties.getObj_max_size()){
            log.error("对象大小超过限制: max {} ,实际 {}", myMinioProperties.getObj_max_size(),file_size);
            throw  new BadObjSize("对象大小超过限制");
        }
        var me = HttpMethod.get("put");
        String res = null;
        try {
            res = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                    .method(me.method)
                    .bucket(myMinioProperties.getBucket())
                    .object(askParam.obj_name())
                    .expiry(1, TimeUnit.HOURS)
                    .extraHeaders(me.headers())
                    .extraHeaders(Map.of("Content-Length",Long.toString(file_size)))
                    .build());
        } catch (ErrorResponseException e) {
            throw new RuntimeException(e);
        } catch (InsufficientDataException e) {
            throw new RuntimeException(e);
        } catch (InternalException e) {
            throw new RuntimeException(e);
        } catch (InvalidKeyException e) {
            throw new RuntimeException(e);
        } catch (InvalidResponseException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        } catch (XmlParserException e) {
            throw new RuntimeException(e);
        } catch (ServerException e) {
            throw new RuntimeException(e);
        }
        //由外部提供主机名,内网情况下提供主机名没有意义,只能提供请求路径,剩下的交给nginx
        res = res.substring(myMinioProperties.getSignature_host().length());
        var xx=new StringBuilder("/minio-service".length()+res.length());
        xx.append("/minio-service");
        xx.append(res);

        return new AskResult("put",xx.toString());
    }
}
